/*****************************
* randomtestcard.c
* Author: Matthew Tanner
* CS362 - Assignment #4
*
* Random test of zzzsmithycardplay function in dominion.c
* Randomizes hand, deck, and discard count.
* Fills hand, deck, and discard piles with random piles.
* Calls oracle checkSmithy to test output.
*
* Output is checked as follows:
*   - Confirms 0, 1, or 2 cards added to hand depending on how many cards in
*		combined deck and discard. Then one discard, so net change is tested
*		to be between -1 and 2.
*	- Confirms 0-3 cards remoaved from deck and discard piles. For some reason,
*		zzzsmithycardplay doesn't result in card discarded ending up in the discard
*		pile. Assuming its taken care of in another function. Hence the function
*		is tested from 0-3 less cards rather than -1 to 2.
*	
* The following bugs have been found on not adressed yet:
*   - none
* 
******************************/

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

#include "dominion.h"
#include "dominion_helpers.h"
#include "rngs.h"
#include "tests.h"

#define NUM_TYPE_CARDS 13  // 3 treasure + 10 adventurer cards

// set this to 1 to get summary of each iteration to stdout
#define NOISY 0

// used to set psuedo random number generator sequence of numbers
#define SEED 100

/*****************************
* Creates random inputs. Parameter numIterations determines how many 
* iterations will be made.
******************************/
void randomtestcard(int numIterations){

	//set minimum req'd variables and initialize game.

	struct gameState g;
	int players = 2;
	int k[10] = {adventurer, council_room, sea_hag, gardens, mine, remodel, smithy, village, baron, great_hall};
	int res, p, n, i, c;

	if((res = initializeGame(players, k, SEED, &g)) == -1){
	printf("Error initializing game\n");
	exit(-1);
	}

	SelectStream(2);
	PutSeed(SEED);

	//repeat this <numIterations> times
	for(n = 0; n < numIterations; n++){

		/***************************************
		* randomly set handcount, deckcount, discardcount from 0 to max values
		* NOTE** program crashes if more than 475 cards provided
		*****************************************/
		p = floor(Random() * 2)+1;
		g.whoseTurn = p;
		
		// Set number of cards in piles. (475 is reduced max piles size to
		// avoid seg fault)
		g.deckCount[p] = floor(Random() * MAX_HAND); 
		g.discardCount[p] = floor(Random() * MAX_HAND); 
		g.handCount[p] = floor(Random() * (MAX_HAND-1)) + 1;   //smithy requires one card   

		// fill deck with random cards (ignores supply count)
		 for(i = 0; i < g.deckCount[p]; i++){
			c = floor(Random() * NUM_TYPE_CARDS);
			g.deck[p][i] = getCard(c, k, &g);
			//printf("%d/%d ", g.deck[p][i], n);
		}

		/// fill discard with random cards (ignores supply count)
		for(i = 0; i < g.discardCount[p]; i++){
			c = floor(Random() * NUM_TYPE_CARDS);
			g.discard[p][i] = getCard(c, k, &g);
			// printf("%d/%d ", g.discard[p][i], n);
		}   

		// fill hand with random cards (ignores supply count)
		for(i = 0; i < g.handCount[p]; i++){
			c = floor(Random() * NUM_TYPE_CARDS);
			//printf("c:%d\n", c);
			g.hand[p][i] = getCard(c, k, &g);
			//printf("%d/%d ", g.hand[p][i], n);
		}

		#if NOISY
			printf("Iteration: %d\n", n);
			printf("Pre Hand:  ");
			for(int i=0; i < g.handCount[p]; i++){
				printf("%d, ", g.hand[p][i]);
			}
			printf("\n");

		    printf("Pre Deck: ");
		    for(int i=0; i < g.deckCount[p]; i++){
		      printf("%d, ", g.deck[p][i]);
		    }
		    printf("\n");

		     printf("Pre Discard: ");
		    for(int i=0; i < g.discardCount[p]; i++){
		      printf("%d, ", g.discard[p][i]);
		    }
		    printf("\n");			
		#endif

		checkSmithy(p, &g);

	}
}

// oracle for random test of zzzsmithycardplay function
void checkSmithy(int p, struct gameState* post){

	struct gameState pre;
	int i, r, diff, prePilesCount, postPilesCount;

	// make copy of gamestate
	memcpy(&pre, post,sizeof(struct gameState));

	// call function (cardeffect calls all card functions)
	r = cardEffect(smithy, 0, p, 0, post, 0, 0);    

	#if NOISY
		printf("...\n");
		printf("Post Hand: ");
		for(int i=0; i < post->handCount[p]; i++){
		  printf("%d, ", post->hand[p][i]);
		}
		printf("\n");

		printf("Post Deck: ");
		for(int i=0; i < post->deckCount[p]; i++){
		  printf("%d, ", post->deck[p][i]);
		}
		printf("\n");

		printf("Post Discard: ");
		for(int i=0; i < post->discardCount[p]; i++){
		  printf("%d, ", post->discard[p][i]);
		}
		printf("\n\n");				
	#endif


	prePilesCount = pre.deckCount[p] + pre.discardCount[p];
	postPilesCount = post->deckCount[p] + post->discardCount[p];
	
	// Assert 2 cards added to hand if 3 or more cards in discard/deck piles,
	// n-1 cards added to hand if less than 3 in piles.
	if(prePilesCount >= 3){
		diff = 2;
	} else diff = (prePilesCount) - 1;
	assert(post->handCount[p] - pre.handCount[p] == diff);
	
	
	// Assert 3 cards less in deck/discard piles (or both empty if < 3)
	if(prePilesCount >= 3){
		diff = 3;	
	} else diff = prePilesCount;
	assert((prePilesCount - postPilesCount) == diff);		

}

	// returns card based on integer input (0-9 adventurer cards, 10-12 treasure cards)
// used to fill piles
enum CARD getCard(int n, int k[], struct gameState* g){

	if(n < 10){
		return k[n];
	}
	else{
		switch(n){
			case 10:
			g->coins += 1;
			return copper;
			case 11:
			g->coins += 2;
			return silver;
			case 12:
			g->coins += 3;
			return gold;
			default:
			return -1;
		}
	}
}

